from playwright.sync_api import Page, expect, TimeoutError as PlaywrightTimeoutError
from common.logger import logger
from common.config import Config
import time


class BasePage:
    """Playwright 页面基类，封装常用操作"""

    def __init__(self, page: Page):
        self.page = page
        self.logger = logger

    def goto(self, url: str) -> None:
        """导航到指定URL"""
        self.page.goto(url)
        self.logger.info(f"导航到: {url}")

    def fill(self, selector: str, value: str) -> None:
        """填充输入框"""
        try:
            self.page.fill(selector, value)
            self.logger.info(f"在元素 {selector} 输入: {value}")
        except Exception as e:
            self.logger.error(f"输入失败: {selector}, 错误: {e}")
            raise

    def type(self, selector: str, value: str, delay: int = 0) -> None:
        """模拟键盘输入"""
        try:
            self.page.type(selector, value, delay=delay)
            self.logger.info(f"在元素 {selector} 输入: {value}")
        except Exception as e:
            self.logger.error(f"输入失败: {selector}, 错误: {e}")
            raise

    def click(self, selector: str) -> None:
        """点击元素"""
        try:
            self.page.click(selector)
            self.logger.info(f"点击元素: {selector}")
        except Exception as e:
            self.logger.error(f"点击失败: {selector}, 错误: {e}")
            raise

    def wait_for_selector(self, selector: str, timeout: int = None) -> None:
        """等待元素出现"""
        if timeout is None:
            timeout = Config.TIMEOUT

        try:
            self.page.wait_for_selector(selector, timeout=timeout)
            self.logger.info(f"元素已出现: {selector}")
        except PlaywrightTimeoutError:
            self.logger.error(f"等待元素超时: {selector}")
            raise

    def wait_for_load_state(self, state: str = "load") -> None:
        """等待页面加载状态"""
        self.page.wait_for_load_state(state)
        self.logger.info(f"页面加载状态: {state}")

    def get_text(self, selector: str) -> str:
        """获取元素文本"""
        try:
            text = self.page.text_content(selector)
            self.logger.info(f"获取元素文本: {selector} -> {text}")
            return text or ""
        except Exception as e:
            self.logger.error(f"获取文本失败: {selector}, 错误: {e}")
            return ""

    def get_attribute(self, selector: str, attribute: str) -> str:
        """获取元素属性"""
        try:
            value = self.page.get_attribute(selector, attribute)
            self.logger.info(f"获取元素属性: {selector}.{attribute} -> {value}")
            return value or ""
        except Exception as e:
            self.logger.error(f"获取属性失败: {selector}.{attribute}, 错误: {e}")
            return ""

    def is_visible(self, selector: str, timeout: int = 5000) -> bool:
        """检查元素是否可见"""
        try:
            self.page.wait_for_selector(selector, state="visible", timeout=timeout)
            return True
        except PlaywrightTimeoutError:
            return False

    def is_hidden(self, selector: str, timeout: int = 5000) -> bool:
        """检查元素是否隐藏"""
        try:
            self.page.wait_for_selector(selector, state="hidden", timeout=timeout)
            return True
        except PlaywrightTimeoutError:
            return False

    def is_enabled(self, selector: str) -> bool:
        """检查元素是否启用"""
        try:
            return self.page.is_enabled(selector)
        except Exception as e:
            self.logger.error(f"检查元素启用状态失败: {selector}, 错误: {e}")
            return False

    def is_checked(self, selector: str) -> bool:
        """检查复选框/单选框是否选中"""
        try:
            return self.page.is_checked(selector)
        except Exception as e:
            self.logger.error(f"检查元素选中状态失败: {selector}, 错误: {e}")
            return False

    def select_option(self, selector: str, value: str) -> None:
        """选择下拉框选项"""
        try:
            self.page.select_option(selector, value=value)
            self.logger.info(f"选择下拉选项: {selector} -> {value}")
        except Exception as e:
            self.logger.error(f"选择下拉选项失败: {selector}, 错误: {e}")
            raise

    def check(self, selector: str) -> None:
        """勾选复选框"""
        try:
            self.page.check(selector)
            self.logger.info(f"勾选元素: {selector}")
        except Exception as e:
            self.logger.error(f"勾选元素失败: {selector}, 错误: {e}")
            raise

    def uncheck(self, selector: str) -> None:
        """取消勾选复选框"""
        try:
            self.page.uncheck(selector)
            self.logger.info(f"取消勾选元素: {selector}")
        except Exception as e:
            self.logger.error(f"取消勾选元素失败: {selector}, 错误: {e}")
            raise

    def hover(self, selector: str) -> None:
        """鼠标悬停"""
        try:
            self.page.hover(selector)
            self.logger.info(f"鼠标悬停: {selector}")
        except Exception as e:
            self.logger.error(f"鼠标悬停失败: {selector}, 错误: {e}")
            raise

    def focus(self, selector: str) -> None:
        """聚焦元素"""
        try:
            self.page.focus(selector)
            self.logger.info(f"聚焦元素: {selector}")
        except Exception as e:
            self.logger.error(f"聚焦元素失败: {selector}, 错误: {e}")
            raise

    def press(self, selector: str, key: str) -> None:
        """按键操作"""
        try:
            self.page.press(selector, key)
            self.logger.info(f"按键操作: {selector} -> {key}")
        except Exception as e:
            self.logger.error(f"按键操作失败: {selector}, 错误: {e}")
            raise

    def screenshot_element(self, selector: str, filename: str) -> str:
        """对元素截图"""
        try:
            screenshot_dir = Config.VERIFY_IMG_DIR
            screenshot_path = screenshot_dir / filename

            # 等待元素出现
            self.wait_for_selector(selector)

            # 对元素截图
            element = self.page.query_selector(selector)
            if element:
                element.screenshot(path=str(screenshot_path))
                self.logger.info(f"元素截图已保存: {screenshot_path}")
                return str(screenshot_path)
            else:
                raise Exception(f"元素未找到: {selector}")
        except Exception as e:
            self.logger.error(f"元素截图失败: {selector}, 错误: {e}")
            raise

    def take_screenshot(self, filename: str) -> str:
        """截取整个页面"""
        try:
            screenshot_dir = Config.BASE_DIR / "screenshots"
            screenshot_dir.mkdir(exist_ok=True)
            screenshot_path = screenshot_dir / filename

            self.page.screenshot(path=str(screenshot_path))
            self.logger.info(f"页面截图已保存: {screenshot_path}")
            return str(screenshot_path)
        except Exception as e:
            self.logger.error(f"页面截图失败: {e}")
            raise

    def get_current_url(self) -> str:
        """获取当前URL"""
        return self.page.url

    def refresh(self) -> None:
        """刷新页面"""
        self.page.reload()
        self.logger.info("页面已刷新")

    def go_back(self) -> None:
        """返回上一页"""
        self.page.go_back()
        self.logger.info("返回上一页")

    def go_forward(self) -> None:
        """前进下一页"""
        self.page.go_forward()
        self.logger.info("前进下一页")

    def wait_for_timeout(self, timeout: int) -> None:
        """等待指定时间"""
        self.page.wait_for_timeout(timeout)
        self.logger.info(f"等待 {timeout}ms")

    def expect_element_visible(self, selector: str) -> None:
        """期望元素可见"""
        expect(self.page.locator(selector)).to_be_visible()

    def expect_element_hidden(self, selector: str) -> None:
        """期望元素隐藏"""
        expect(self.page.locator(selector)).to_be_hidden()

    def expect_text_to_be_visible(self, text: str) -> None:
        """期望文本可见"""
        expect(self.page.get_by_text(text)).to_be_visible()

    def retry_action(self, action, max_retries: int = None, delay: float = None):
        """重试装饰器"""
        if max_retries is None:
            max_retries = Config.MAX_RETRY
        if delay is None:
            delay = Config.RETRY_DELAY

        def wrapper(*args, **kwargs):
            for attempt in range(max_retries):
                try:
                    return action(*args, **kwargs)
                except Exception as e:
                    if attempt == max_retries - 1:
                        raise e
                    self.logger.warning(f"操作失败，第{attempt + 1}次重试: {e}")
                    time.sleep(delay)

        return wrapper